home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Light ROM 1
/
LIGHT-ROM 1 (Amiga Library Services)(1994).iso
/
ffdisks
/
d879.lha
/
DiskTest
/
Source
/
dt.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-04-06
|
20KB
|
776 lines
/*-----------------------------------------*
| File: DT.c - Main disk testing routines |
+-----------------------------------------+---------------*
| Author: Maurizio Loreti, aka MLO or I3NOO. |
| Address: University of Padova - Department of Physics |
| Via F. Marzolo, 8 - 35131 PADOVA - Italy |
| Phone: (39)(49) 844-313 FAX: (39)(49) 844-245 |
| E-Mail: loreti@padova.infn.it (TCP/IP) |
| Home: Via G. Donizetti 6 - 35010 CADONEGHE (PD) - Italy |
*---------------------------------------------------------*/
/**
| #includes
**/
#include <stddef.h> /* Standard library */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <exec/types.h> /* Amiga specific */
#include <exec/memory.h>
#include <devices/trackdisk.h>
#include <workbench/startup.h>
#include <clib/exec_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/asl_protos.h>
#include <clib/intuition_protos.h>
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>
#include <clib/wb_protos.h>
#include "main.h"
#include "dt.h" /* Local stuff */
#include "ext.h"
void EventLoop(void)
{
/**
| Main event loop.
| First, we initialise the AppWindow environment; then we Wait()
| for a signal from IDCMP or the AppWindow, and take the appropriate
| action.
**/
ULONG signalSet;
if ((appWinPort = CreateMsgPort()) == NULL) {
Error("Error from CreateMsgPort");
}
if ((pAWind = AddAppWindow(AW_ID, 0, pWind, appWinPort,
TAG_END)) == NULL) {
Error("Error from AddAppWindow");
}
signalSet =
(1L << appWinPort->mp_SigBit) | (1L << pWind->UserPort->mp_SigBit);
FOREVER {
struct IntuiMessage *pIM;
struct AppMessage *pAM;
(void) Wait(signalSet);
while ((pIM = GT_GetIMsg(pWind->UserPort)) != NULL) {
ULONG class;
UWORD code;
struct Gadget *pG;
class = pIM->Class;
code = pIM->Code;
pG = (struct Gadget *) pIM->IAddress;
GT_ReplyIMsg(pIM);
switch (class) {
case GADGETDOWN:
case GADGETUP:
switch (pG->GadgetID) {
case BUT_QUIT:
Cleanup();
break;
case BUT_1:
case BUT_2:
case BUT_3:
case BUT_4:
StartTest(0);
DiskCheck(pG->GadgetID);
EndTest();
break;
case BUT_FN:
/* NO see RKM listFilenames = (BOOL)(pG->Flags & GFLG_SELECTED; */
listFileNames = (BOOL)(!listFileNames);
break;
case SCROLLER:
SetTopLine(code);
break;
default:
break;
}
break;
case MOUSEMOVE:
SetTopLine(code);
break;
case REFRESHWINDOW:
GT_BeginRefresh(pWind);
RefreshView(TRUE);
GT_EndRefresh(pWind, TRUE);
break;
case MENUPICK:
while (code != MENUNULL) {
struct MenuItem *pMI;
int menuFlag;
pMI = ItemAddress(pMenu, code);
menuFlag = (int) GTMENUITEM_USERDATA(pMI);
ActionMenu(menuFlag);
code = pMI->NextSelect;
}
break;
default:
break;
}
}
while ((pAM = (struct AppMessage *) GetMsg(appWinPort)) != NULL) {
int i;
BPTR dirLock;
struct WBArg *pWBA;
char dirName[PATHNAME_MAX];
char filName[FILENAME_MAX];
for (i=0, pWBA=pAM->am_ArgList; i<pAM->am_NumArgs; i++, pWBA++) {
if (i) AddLine("", 0, TRUE);
strcpy(filName, pWBA->wa_Name);
if (filName[0]) {
dirLock = DupLock(pWBA->wa_Lock);
} else {
(void) NameFromLock(pWBA->wa_Lock,
dirName, PATHNAME_MAX);
}
if (filName[0]) {
FileStuff(dirLock, NULL, filName, i);
} else {
DirStuff(dirName, i);
}
}
ReplyMsg((struct Message *) pAM);
}
}
}
/*-------------------------- Local Procedures --------------------------*/
static void ActionMenu(
const int code
){
/**
| This procedure handles all the requests made using a menu
**/
switch (code) {
case M_ABOUT:
{
struct EasyStruct aboutES = {
sizeof(struct EasyStruct), 0,
PROG_NAME " message:",
"DiskTest -- %s\nAuthor: Maurizio Loreti (MLO)\n"
"EMail: loreti@padova.infn.it",
"OK"
};
(void) EasyRequest(pWind, &aboutES, NULL, VersionTag+7);
}
break;
case M_QUIT:
Cleanup();
break;
case M_DF0:
case M_DF1:
case M_DF2:
case M_DF3:
StartTest(0);
DiskCheck(code);
EndTest();
break;
case M_DEVDIR:
if (AslRequestTags(pFR, ASL_ExtFlags1, FIL1F_NOFILES, TAG_END)) {
DirStuff(pFR->rf_Dir, 0);
}
break;
case M_FILE:
if (AslRequestTags(pFR, ASL_ExtFlags1, 0, TAG_END)) {
int i;
BPTR dLock;
struct WBArg *pWBA;
if (dLock = Lock(pFR->rf_Dir, ACCESS_READ)) {
for (i=0, pWBA=pFR->rf_ArgList; i<pFR->rf_NumArgs; i++, pWBA++) {
if (i) AddLine("", 0, TRUE);
FileStuff(dLock, pFR->rf_Dir, pWBA->wa_Name, i);
}
UnLock(dLock);
}
}
break;
default:
break;
}
}
static unsigned CheckBreak(void)
{
/**
| Called from time to time to check for user breaks; checks the
| CTRL-C signal (actually unused) and the "break window" gadgets.
| The returned value is the "abortDT" global variable itself.
**/
if (!abortDT &&
(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C))
abortDT |= BRK_DETECTED;
if (pBWind != NULL) {
struct IntuiMessage *pIM;
if ((pIM = GT_GetIMsg(pBWind->UserPort)) != NULL) {
GT_ReplyIMsg(pIM);
abortDT |= BRK_DETECTED;
}
}
if (abortDT && !(abortDT & WARN_PRINTED)) {
OutLine(FALSE, "*** DiskTest: BREAK ***");
abortDT |= WARN_PRINTED;
}
return abortDT;
}
static void CheckDir(
char *path,
const BOOL root
){
/**
| This procedure checks (recursively) a directory.
| "path" contains the full directory name, and "root" is non-zero if
| this directory is at the top level (the difference is in the special
| handling of the ':', and in the replacement of the device name (e.g.
| "DH0:") with its label (e.g. "Workbench:"), in general more meaningful.
|
| CheckDir() scans the wanted directory, checking immediately the 'true'
| files; the subdirectories (if any) are checked recursively at the
| end, one by one.
| If an error is detected from CheckDir(), the directory/file test is
| stopped and the global flag "abortDT" is set; this makes possible, in
| the further steps, to recursively free() all the memory that has been
| allocated in order to store the subdirectory names.
|
| First: obtain a lock on the wanted directory, and Examine() the lock;
| since only one directory is being scanned at a time, we can use a
| single FileInfoBlock global buffer.
**/
dirEntry *rdE = NULL;
BPTR dlock;
if ((dlock = Lock(path, ACCESS_READ)) == 0) {
OutLine(FALSE, "* Can't access directory %s !", path);
abortDT = INTERNAL_ERR;
nErFil++;
} else {
if (!Examine(dlock, pFIB)) {
OutLine(FALSE,
"* Error return from Examine(), directory %s", path);
abortDT = INTERNAL_ERR;
nErFil++;
} else {
/**
| Prepare in "fileName" the full directory name - to which local
| filenames will be appended.
**/
char fileName[PATHNAME_MAX + FILENAME_MAX];
char *pc;
if (root) {
pc = strcpy(fileName, pFIB->fib_FileName);
pc += strlen(fileName);
*pc++ = ':';
*pc = '\0';
OutLine(FALSE,
" checking files in root directory %s ...", fileName);
} else {
pc = strcpy(fileName, path);
pc += strlen(fileName);
OutLine(FALSE,
" checking files in directory %s ...", fileName);
*pc++ = '/';
}
nDirs++;
/**
| Now, loop over all directory entries. As already said, all the
| 'real' files are immediately checked; the subdirectory names are
| stored in a linked list to be examined at the end. This list is
| implemented as a LIFO tree (the simplest type).
**/
while (ExNext(dlock, pFIB)) {
(void) strcpy(pc, pFIB->fib_FileName);
if (pFIB->fib_DirEntryType < 0) {
CheckFile(fileName);
} else {
/**
| If a memory allocation error is detected when asking space for
| our linked list, we exit the "while" loop; setting the "abortDT"
| flag before exiting ensures that all the memory we had from
| these malloc()'s will later be free()-ed recursively.
**/
dirEntry *pdE;
if ((pdE = malloc(sizeof(dirEntry) + strlen(fileName))) == NULL) {
OutLine(FALSE, "* Can't allocate heap memory!");
abortDT = INTERNAL_ERR;
break;
}
(void) strcpy(pdE->name, fileName);
pdE->next = rdE;
rdE = pdE;
}
if (CheckBreak()) break;
}
/**
| We now check if ExNext() has failed, or if the last
| directory entry has been found.
**/
if (!abortDT) {
int errno;
if ((errno = IoErr()) != ERROR_NO_MORE_ENTRIES) {
OutLine(FALSE, "* Error %d reading directory %s !",
errno, path);
nErFil++;
}
}
}
UnLock(dlock);
}
/**
| Now, we loop over all detected subdirectories (if any);
| freeing in the same time the memory used to store their names.
**/
while (rdE != NULL) {
dirEntry *pdE;
if (!abortDT) CheckDir(rdE->name, FALSE);
pdE = rdE->next;
free(rdE);
rdE = pdE;
}
}
static void CheckFile(
char *name
){
/**
| Check a file, opening and reading it record by record.
**/
BPTR pFH;
nFiles++;
if (listFileNames) OutLine(FALSE, " file %s ...", name);
if ((pFH = Open(name, MODE_OLDFILE)) == 0) {
OutLine(FALSE, "* Error %d opening file \"%s\".", IoErr(), name);
nErFil++;
} else {
long ier;
while (!abortDT &&
(ier = Read(pFH, diskBuffer, (long) diskBufferLength)) > 0) {
(void) CheckBreak();
}
(void) Close(pFH);
if (ier < 0) {
OutLine(FALSE, "* Error %d reading file \"%s\".", IoErr(), name);
nErFil++;
}
}
}
static void DirStuff(
char *name,
int i
){
/**
| The driver for a device/directory integrity test.
| We allocate a buffer and clear the scroller, then
| call CheckDir in order to perform the actual test.
| If i is zero, the scroller is cleared.
**/
char *pc;
GetBuffer(FILBUF_DIM);
StartTest(i);
OutLine(FALSE, "File integrity check ...");
pc = name + strlen(name) - 1;
CheckDir(name, (BOOL)(*pc == ':'));
EndTest();
}
static void DiskCheck(
const int drive
){
/**
| Full test for the floppy unit "drive".
| Properly opened the device, we check for a disk in drive and
| obtain the floppy disk parameters from various status commands;
| then we allocate the memory for our internal buffers.
**/
char driveName[] = "DF0:";
int error;
int cyl;
int head;
ULONG total;
if ((diskPort = CreatePort(NULL, 0L)) == NULL) {
Error("Error from CreatePort");
}
if ((diskReq = (struct IOExtTD *)
CreateExtIO(diskPort, sizeof(struct IOExtTD))) == NULL) {
Error("Error from CreateExtIo");
}
driveName[2] += (char) drive;
if ((error =
OpenDevice(TD_NAME, (ULONG) drive,
(struct IORequest *) diskReq, 0L)) != 0) {
sprintf(slate, "Error 0x%X from OpenDevice", error);
Error(slate);
}
diskReq->iotd_Req.io_Command = TD_CHANGESTATE;
(void) DoIO((struct IORequest *) diskReq);
if (diskReq->iotd_Req.io_Actual) {
OutLine(FALSE, "No disk in drive %d ...", drive);
return;
}
diskReq->iotd_Req.io_Command = TD_CHANGENUM;
(void) DoIO((struct IORequest *) diskReq);
diskReq->iotd_Req.io_Command = TD_GETGEOMETRY;
diskReq->iotd_Req.io_Data = &drGeom;
(void) DoIO((struct IORequest *) diskReq);
cylSize = drGeom.dg_TrackSectors * drGeom.dg_SectorSize;
GetBuffer(cylSize);
total = drGeom.dg_TotalSectors * drGeom.dg_SectorSize;
total >>= 10;
OutLine(FALSE,
"Change number for drive %s (%ld KBytes) is %d;",
driveName, total,
(diskChangeCount = diskReq->iotd_Req.io_Actual));
#ifdef DT_DEBUG
OutLine(FALSE, "%ld heads, %ld cylinders",
drGeom.dg_Heads, drGeom.dg_Cylinders);
OutLine(FALSE, "%ld sec/cyl, %ld sec/trk, %ld bytes/sec",
drGeom.dg_CylSectors, drGeom.dg_TrackSectors,
drGeom.dg_SectorSize);
OutLine(FALSE, "%ld total sectors, %ld bytes/trk",
drGeom.dg_TotalSectors, cylSize);
#endif
/**
| Pass 1
| Seek over the floppy.
**/
Motor(ON);
SeekFullRange(1);
if (nErFil) {
Motor(OFF);
OutLine(FALSE, "* %d hard error%s detected seeking drive %s",
nErFil, (nErFil == 1 ? "" : "s"), driveName);
return;
} else {
OutLine(FALSE, " no errors detected seeking over full disk range.");
}
/**
| Pass 2
| Read all disk tracks.
**/
OutLine(FALSE, "Checking all disk tracks:");
for (cyl=0; cyl<(int)drGeom.dg_Cylinders; cyl++) {
for (head=0; head<(int)drGeom.dg_Heads; head++) {
OutLine(TRUE,
" reading cylinder %d, head %d ...", cyl, head);
if (CheckBreak()) {
Motor(OFF);
return;
}
ReadCyl(cyl, head);
if ((error = diskReq->iotd_Req.io_Error) != 0) {
OutLine(FALSE, "* Error 0x%X detected for cylinder %d, head %d",
error, cyl, head);
nErFil++;
}
}
}
Motor(OFF);
if (nErFil) {
OutLine(FALSE, "* %d hard error%s detected reading drive %s.",
nErFil, (nErFil == 1 ? "" : "s"), driveName);
return;
} else {
OutLine(FALSE, " no errors detected reading drive %s.", driveName);
}
/**
| Pass 3
| File integrity check.
**/
OutLine(FALSE, "Checking all files in drive %s", driveName);
CheckDir(driveName, TRUE);
}
static void EndTest(void)
{
/**
| This procedure outputs some statistics on the test;
| and frees all unneeded resources. The buffer only is
| retained, just in case we want to check some other thing...
**/
if (pBWind != NULL) {
struct IntuiMessage *pIM;
while ((pIM = GT_GetIMsg(pBWind->UserPort)) != NULL) {
GT_ReplyIMsg(pIM);
}
CloseWindow(pBWind);
pBWind = NULL;
}
if (diskReq != NULL) {
CloseDevice((struct IORequest *) diskReq);
DeleteExtIO((struct IORequest *) diskReq);
diskReq = NULL;
}
if (diskPort != NULL) {
DeletePort(diskPort);
diskPort = NULL;
}
if (nDirs + nFiles)
OutLine(FALSE,
"%d director%s and %d file%s checked: %d error%s detected.",
nDirs, (nDirs == 1 ? "y" : "ies"),
nFiles, (nFiles == 1 ? "" : "s"),
nErFil, (nErFil == 1 ? "" : "s"));
BusyState(FALSE);
}
static void FileStuff(
BPTR dirLock,
char *dirName,
char *filName,
int i
){
/**
| The driver for the integrity test on a single file.
| We allocate a buffer and clear the scroller; then CD to
| the destination directory, call CheckFile in order to
| perform the actual test, and CD back.
| If dirName is NULL or contains the NULL string, the directory
| name is read from dirLock.
| If i is zero, the scroller is cleared.
**/
BPTR oldLock;
char name[FILENAME_MAX];
GetBuffer(FILBUF_DIM);
StartTest(i);
OutLine(FALSE, "File integrity check ...");
oldLock = CurrentDir(dirLock);
if (dirName == NULL || *dirName == '\0') {
(void) NameFromLock(dirLock, (dirName = name), PATHNAME_MAX);
}
OutLine(FALSE, " Sitting in directory %s ...", dirName);
CheckFile(filName);
(void) CurrentDir(oldLock);
EndTest();
}
static void GetBuffer(
const ULONG size
){
/**
| If no buffer has been previously allocated, GetBuffer() queries
| for memory from the heap; if the previously allocated buffer is
| too small, it is released and reallocated.
| N.B.: we ask for *CHIP* memory, because the buffer has to be used
| for disk reading too; for file reading, MEMF_PUBLIC should
| be enough.
**/
if (diskBuffer != NULL) {
if (diskBufferLength >= size) return;
FreeMem(diskBuffer, diskBufferLength);
}
if ((diskBuffer = AllocMem(size, MEMF_CHIP)) == NULL) {
Error("Error from AllocMem");
}
diskBufferLength = size;
}
static void Motor(
const ULONG action
){
/**
| Issues to the drive a MOTOR ON or OFF request; "action"
| is the preprocessor symbol ON or OFF.
**/
diskReq->iotd_Req.io_Length = action;
diskReq->iotd_Req.io_Command = TD_MOTOR;
(void) DoIO((struct IORequest *) diskReq);
}
static void OutLine(
const BOOL last,
char *fmt,
...
){
/**
| Formatted output on the screen; if "last" is non zero, the
| "special mode" for the output to be discarded is used.
| Currently only the track test calls OutLine() with last=TRUE.
**/
va_list vl;
size_t n;
va_start(vl, fmt);
n = (size_t) vsprintf(slate, fmt, vl);
va_end(vl);
if (last) {
LastLine(slate, n);
} else {
AddLine(slate, n, TRUE);
}
}
static void ReadCyl(
const int cyl,
const int hd
){
/**
| Issues the command to read a whole cylinder on the
| internal buffer.
**/
diskReq->iotd_Req.io_Length = cylSize;
diskReq->iotd_Req.io_Data = (APTR) diskBuffer;
diskReq->iotd_Req.io_Command = ETD_READ;
diskReq->iotd_Count = diskChangeCount;
diskReq->iotd_Req.io_Offset =
drGeom.dg_SectorSize * (drGeom.dg_TrackSectors *
((ULONG) hd + drGeom.dg_Heads * (ULONG) cyl));
(void) DoIO((struct IORequest *) diskReq);
}
static void SeekFullRange(
const SHORT howmany
){
/**
| Performs "howmany" seeks first to the highest
| cylinder of the drive, and then to the lowest.
**/
int i;
SHORT error;
for (i=1; i<=howmany; i++) {
diskReq->iotd_Req.io_Offset =
((drGeom.dg_Cylinders - 1) * drGeom.dg_TrackSectors *
drGeom.dg_Heads - 1) * drGeom.dg_SectorSize;
diskReq->iotd_Req.io_Command = TD_SEEK;
(void) DoIO((struct IORequest *) diskReq);
if ((error = diskReq -> iotd_Req.io_Error) != 0) {
OutLine(FALSE, "* Seek cycle %d, error 0x%X ...", i, error);
nErFil++;
}
diskReq->iotd_Req.io_Offset = 0;
diskReq->iotd_Req.io_Command = TD_SEEK;
(void) DoIO((struct IORequest *) diskReq);
if ((error = diskReq->iotd_Req.io_Error) != 0) {
OutLine(FALSE, "* Seek cycle %d, error 0x%X ...", i, error);
nErFil++;
}
}
}
static void StartTest(
int i
){
/**
| Zeroes the statistics variables, the break flag,
| and clears the output window. Then creates the
| "break" window.
| If i is zero, the scroller is cleared.
**/
nDirs = nFiles = nErFil = 0;
abortDT = 0;
if (!i) ClearText(TRUE);
BusyState(TRUE);
if ((pBWind = OpenWindowTags(NULL,
WA_Left, pWind->LeftEdge + bkwLeft,
WA_Top, pWind->TopEdge + bkwTop,
WA_Width, bkwWidth,
WA_Height, bkwHeight,
WA_Activate, TRUE,
WA_DragBar, TRUE,
WA_DepthGadget, TRUE,
WA_SmartRefresh, TRUE,
WA_IDCMP, BKW_IDCMP,
WA_Title, PROG_NAME " v" REVISION,
WA_Gadgets, pBGad,
WA_PubScreen, pScr,
TAG_DONE)) == NULL) {
Error("Couldn't open the break window");
}
GT_RefreshWindow(pBWind, NULL);
}